package hiresoft.network;

import java.net.Socket;
import java.io.IOException;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;

/**
 * Handles communication with the server.
 * Saves the preferences for the IP address and
 * the Port number in a {@link Preferences} file.
 *
 * @author Filip Jaros
 */
public final class Network
{
    /**
     * The socket for network communication
     */
    private Socket sock;
    /**
     * The stream for communication to the server
     */
    public NetOutputStream out;
    /**
     * The stream for communication from the server
     */
    public NetInputStream in;
    /*
     * The preferences key for the port number
     */
    private static final String PORT_ID = "portnumber";
    /*
     * The preferences key for the IP address
     */
    private static final String IP_ID = "ipaddress";
    /*
     * The node path for the preferences module
     */
    private static final String nodeName = "/com/network";
    /*
     * The preferences module for IP and Port
     */
    private static Preferences prefs = Preferences.userRoot().node(nodeName);
    
    /**
     * Constructor for Network.
     * Attempts to create a socket for
     * communicating with the server and
     * establish the input and output stream.
     */
    private Network()
    {
        setUpSocket();
    }

    /**
     * Returns the current server IP address
     * as a string.  If no Preferences
     * file exists, it returns a default.
     *
     * @return the IP address for the server
     */
    public static String getIP(){
        return prefs.get(IP_ID, "184.82.238.76");
    }

    /**
     * Returns the current port for communicating
     * with the server as an integer.
     * If no preferences file exists,
     * it returns a default
     *
     * @return the Port number for communicating with the server
     */
    public static int getPort(){
        return prefs.getInt(PORT_ID, 19898);
    }

    /**
     * Sets the IP for the server and
     * stores it in the preferences file.
     *
     * @param ip the new IP address for the server
     * @throws BackingStoreException if the preference is not saved
     */
    public static void setDefaultIP(String ip) throws BackingStoreException{
        prefs.put(IP_ID, ip);
        prefs.flush();
        Network.getInstance().setUpSocket();
    }

    /**
     * Sets the port number for
     * communication with the server.
     *
     * @param port the new port for the server
     * @throws BackingStoreException if the preference is not saved
     */
    public static void setDefaultPort(int port) throws BackingStoreException{
        prefs.putInt(PORT_ID, port);
        prefs.flush();
        Network.getInstance().setUpSocket();
    }

    private void setUpSocket(){
        try{
            sock = new Socket(getIP(), getPort());
            in = new NetInputStream(sock.getInputStream());
            out = new NetOutputStream(sock.getOutputStream());
        }catch(IOException e){

        }
    }

    /**
     * To be called when waiting for a response
     * from the server.  Enters a while loop that
     * does not break until the server is
     * ready or after 5 seconds.
     *
     * @return true when the packet is available
     * @throws IOException if a packet is unavailable after five seconds
     */
    public boolean Ready() throws IOException
    {
        int timeout = 0;
        try
        {
            for (;;)
            {
                Thread.sleep(1);
                if (in.packetAvailable())
                    return true;
                if (++timeout == 5000)
                    throw new IOException("Network timed out!");
            }
        }
        catch (Exception e)
        {
            throw new IOException(e.toString());
        }
    }

    /**
     * Returns an instance of the Network class.
     * Used to maintain a singleton of Network.
     *
     * @return the instance of Network
     */
    public static Network getInstance(){
        return NetworkHolder.INSTANCE;
    }

    /**
     * The holder class for the {@link Network}
     * instance.
     */
    private static class NetworkHolder{
        /**
         * The instance of {@link Network}.
         * Instantiates a new Network object the
         * first time it is called.
         */
        private static final Network INSTANCE = new Network();
    }


 
}